package com.netkiller.mailmonitor.workflow.impl.task;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;

import com.netkiller.mailmonitor.dao.MailReportDao;
import com.netkiller.mailmonitor.entity.BlobHistory;
import com.netkiller.mailmonitor.entity.LogMessage;
import com.netkiller.mailmonitor.entity.MailReport;
import com.netkiller.mailmonitor.service.AppFileService;
import com.netkiller.mailmonitor.service.AppUserService;
import com.netkiller.mailmonitor.service.BlobHistoryService;
import com.netkiller.mailmonitor.service.ContextIOService;
import com.netkiller.mailmonitor.service.LogMessageService;
import com.netkiller.mailmonitor.service.OauthService;
import com.netkiller.mailmonitor.util.UserUtil;
import com.netkiller.mailmonitor.workflow.AbstractWorkflowTask;
import com.netkiller.mailmonitor.workflow.WorkflowContext;
import com.netkiller.mailmonitor.workflow.WorkflowExecutionException;
import com.netkiller.mailmonitor.workflow.impl.context.ReportGenerationContext;

public class ReportGenerationTask extends AbstractWorkflowTask {
	protected final Logger logger = Logger.getLogger(getClass().getName());

	@Autowired
	OauthService oauthService;

	@Autowired
	private LogMessageService logMessageService;

	@Autowired
	AppFileService appFileService;

	@Autowired
	AppUserService appUserService;

	@Autowired
	private ContextIOService contextIOService;

	@Autowired
	BlobHistoryService blobHistoryService;

	@Autowired
	MailReportDao mailReportDao;

	static int runs = 0;

	static int usersProcessed = 0;

	static boolean isRunning = false;

	static Set<String> users = new HashSet<String>();

	public static void initTask() {
		users.clear();
		usersProcessed = 0;
		isRunning = true;
	}

	public static Boolean isTaskRunning() {
		return isRunning;
	}

	@Override
	public WorkflowContext execute(WorkflowContext context)
			throws WorkflowExecutionException {
		ReportGenerationContext thisContext = (ReportGenerationContext) context;
		String adminEmail = thisContext.getAdminEmail();
		String userEmail = thisContext.getUserEmail();
		LogMessage logMessage = new LogMessage();
		Date fromDate = thisContext.getFromDate();
		Date toDate = thisContext.getToDate();
		int totalAccounts = thisContext.getTotalAccounts();
		String month = getMonth(fromDate);
		// if (users.add(userEmail)) {
		try {
			generateReport(userEmail, adminEmail, fromDate, toDate, month);
		} catch (Exception e) {
			logMessage.setMessage("Could not fetch details for  " + userEmail);
			logMessageService.save(logMessage);
			System.out.println("Error occurred : " + e.getMessage());
			if (e.getCause() != null) {
				System.out.println(e.getCause().getMessage());
			}
		} finally {
			synchronized (this) {
				usersProcessed++;
				// System.out.println(userEmail + " \tusersProcessed : "+
				// usersProcessed + "\n totalAccounts : " + totalAccounts);

				if (totalAccounts == usersProcessed) {
					usersProcessed = 0;
					isRunning = false;
					saveFileToBlobStore(adminEmail, month);
				}
			}
		}
		// }

		System.out.println("Report task finished for " + userEmail
				+ ". No. of runs :" + ++runs);
		return context;
	}

	void saveFileToBlobStore(String email, String month) {
		try {
			Thread.sleep(4500);
			String fileName = email + "_" + new Date() + ".csv";
			String domain = UserUtil.getDomain(email);
			List<MailReport> mailReports = mailReportDao.getAll();
			String report = convertMailReportsToCsv(mailReports);
			String blobKeyStr = appFileService.saveFileToBlobStore(report,
					fileName);
			createBlobHistory(blobKeyStr, email, domain, fileName, month);
			LogMessage logMessage = new LogMessage(
					"Report is available for Download");
			logMessageService.save(logMessage);
			mailReportDao.removeAll(mailReports);
		} catch (Exception e2) {
			System.out.println("Report File Creation Failed");
			LogMessage logMessage1 = new LogMessage(
					"Report File Creation Failed");
			logMessageService.save(logMessage1);
		}
	}

	private void createBlobHistory(String blobKeyStr, String adminEmail,
			String domain, String fileName, String month) {
		BlobHistory blobHistory = new BlobHistory();
		blobHistory.setBlobKeyStr(blobKeyStr);
		blobHistory.setCreatedBy(adminEmail);
		blobHistory.setDomainName(domain);
		blobHistory.setFileName(fileName);
		blobHistory.setMonth(month);
		blobHistoryService.save(blobHistory);
	}

	private String getMonth(Date date) {
		Calendar calendar = new GregorianCalendar();
		if (date != null) {
			calendar.setTime(date);
		}
		return calendar.getDisplayName(Calendar.MONTH, Calendar.MONTH,
				Locale.getDefault());
	}

	private String generateReport(String user, String email, Date fromDate,
			Date toDate, String month) throws JSONException {

		String response = null;
		for (int i = 0; i < 5; i++) {
			try {
				response = contextIOService.fetchMailsByUser(user, fromDate,
						toDate);
				break;
			} catch (Exception e) {
				System.out.println(e.getMessage() + " will retry in 5 secs");
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				// TODO: handle exception
			}
		}
		// System.out.println(user + " : " + response);
		MailReport mailReport = generateMailReport(response, user, month);
		mailReportDao.create(mailReport);

		return response;
	}

	private String convertMailReportsToCsv(List<MailReport> mailReports) {
		StringBuilder report = new StringBuilder(
				"Email,Month,Sent(Same domain),Sent(External domains), Received(Same domain),Received(External domain),Status\n");
		for (MailReport mailReport : mailReports) {
			report.append(mailReport.getEmail() + "," + mailReport.getMonth()
					+ "," + mailReport.getSentToSameDomain() + ","
					+ mailReport.getSentToOtherDomain() + ","
					+ mailReport.getReceivedFromSameDomain() + ","
					+ mailReport.getReceivedFromOtherDomain() + ","
					+ mailReport.getMessage() + "\n");
		}

		return report.toString();
	}

	private MailReport generateMailReport(String response, String email,
			String month) throws JSONException {
		MailReport mailReport = new MailReport();
		mailReport.setEmail(email);
		mailReport.setMonth(month);
		String domainName = UserUtil.getDomain(email);
		if (!StringUtils.isBlank(response)) {
			JSONArray jsonArray = new JSONArray(response);
			System.out.println("::::::Mails fetched : " + jsonArray.length());
			for (int i = 0; i < jsonArray.length(); i++) {
				JSONObject thisJsonObject = jsonArray.getJSONObject(i);
				try {
					boolean notSpam = true;
					//System.out.println(thisJsonObject);
					if (thisJsonObject.has("folders")) {
						JSONArray folderArray = thisJsonObject
								.getJSONArray("folders");
						for (int k = 0; k < folderArray.length(); k++) {
							if (folderArray.getString(k).equals("[Gmail]/Spam") || folderArray.getString(k).equals("\\Spam")) {
//								System.out.println("skipping spam message :" + thisJsonObject.getString("subject"));
								notSpam= false;
								break;
							}
						}
					}
					if (notSpam && thisJsonObject.has("addresses")) {
						JSONObject addresses = thisJsonObject
								.getJSONObject("addresses");
						JSONObject from = addresses.getJSONObject("from");
						String sender = from.getString("email");
						if (sender.equalsIgnoreCase(email)) {
							fetchSentMails(thisJsonObject, domainName, sender,
									mailReport);
						} else {
							fetchReceivedMails(thisJsonObject, domainName,
									mailReport);
						}
						Date date = new Date();
						date.setTime(thisJsonObject.getLong("date") * 1000);
						System.out.println("date : " + date);
						System.out.println("subject : "
								+ thisJsonObject.getString("subject"));
						System.out.println("address : "
								+ thisJsonObject.getString("addresses"));
						//System.out.println(mailReport);
						System.out.println("---------------------");
					}
				} catch (Exception e) {
					System.out.println("could not process : " + thisJsonObject);
				}

			}

		} else {
			mailReport.setMessage("User not synced or Error connecting");
		}
		return mailReport;
	}

	void fetchSentMails(JSONObject obj, String domainName, String sender,
			MailReport mailReport) throws JSONException {
		JSONObject addresses = new JSONObject(obj.get("addresses").toString());
		if (addresses.has("to")) {
			JSONArray to = addresses.getJSONArray("to");
			updateSentCounts(to, sender, domainName, mailReport);
		}
		if (addresses.has("cc")) {
			JSONArray cc = addresses.getJSONArray("cc");
			updateSentCounts(cc, sender, domainName, mailReport);
		}

		if (addresses.has("bcc")) {
			JSONArray bcc = addresses.getJSONArray("bcc");
			updateSentCounts(bcc, sender, domainName, mailReport);
		}

	}

	void updateSentCounts(JSONArray jsonArray, String sender,
			String domainName, MailReport mailReport) throws JSONException {
		int sentToSameDomain = mailReport.getSentToSameDomain();
		int sentToOtherDomain = mailReport.getSentToOtherDomain();
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObject = jsonArray.getJSONObject(i);
			String email = jsonObject.getString("email");
			if (email.contains(domainName)) {
				sentToSameDomain++;
			} else {
				sentToOtherDomain++;
			}

			if (email.equals(sender)) {
				int receivedFromSameDomain = mailReport
						.getReceivedFromSameDomain() + 1;
				mailReport.setReceivedFromSameDomain(receivedFromSameDomain);
			}

		}
		mailReport.setSentToSameDomain(sentToSameDomain);
		mailReport.setSentToOtherDomain(sentToOtherDomain);
	}

	void fetchReceivedMails(JSONObject obj, String domainName,
			MailReport mailReport) throws JSONException {
		// long msgDate = obj.getLong("date");
		JSONObject addresses = new JSONObject(obj.get("addresses").toString());
		JSONObject from = addresses.getJSONObject("from");
		updateReceivedCounts(from.getString("email"), domainName, mailReport);

	}

	void updateReceivedCounts(String email, String domainName,
			MailReport mailReport) throws JSONException {
		if (email.contains(domainName)) {
			int receivedFromSameDomain = mailReport.getReceivedFromSameDomain() + 1;
			mailReport.setReceivedFromSameDomain(receivedFromSameDomain);
		} else {
			int receivedFromOtherDomain = mailReport
					.getReceivedFromOtherDomain() + 1;
			mailReport.setReceivedFromOtherDomain(receivedFromOtherDomain);
		}

	}
}
